package com.jl.card.cmd;

import java.util.ArrayList;
import java.util.List;

import opencard.core.terminal.CommandAPDU;
import opencard.core.terminal.ResponseAPDU;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.annotations.JSFunction;

import com.jl.card.ICard;
import com.jl.card.JL;
import com.jl.util.HexUtil;

public class CardCommandNoRuntimeException extends ScriptableObject {

	private static final long serialVersionUID = -4149674922039459832L;
	private ICard card;
	private List<ICmdAndExpLog> list = new ArrayList<ICmdAndExpLog>();

	public CardCommandNoRuntimeException() {
	}

	public CardCommandNoRuntimeException(Object card) {
		if (card instanceof ICard) {
			this.card = (ICard) card;
		} else {
			this.card = (ICard) Context.jsToJava(card, ICard.class);
		}
	}

	@JSFunction
	public void select(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_SELECT_FILE, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void status(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_STATUS, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void readBinary(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_READ_BINARY, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void updateBinary(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_UPDATE_BINARY, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void readRecord(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_READ_RECORD, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void updateRecord(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_UPDATE_RECORD, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void searchRecord(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_SEARCH_RECORD, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void increse(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_INCREASE, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void verifyPIN(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_VERIFY, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void changePIN(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_CHANGE_CHV, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void disablePIN(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_DISABLE_CHV, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void enablePIN(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_ENABLE_CHV, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void unblockPIN(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_UNBLOCK_CHV, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void deactivateFile(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_DEACTIVATE_FILE, p1, p2, data, le, expData,
				expSW);
	}

	@JSFunction
	public void activateFile(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_ACTIVATE_FILE, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void authenticate(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_AUTHENTICATE, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void manageChannel(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_MANAGE_CHANNEL, p1, p2, data, le, expData,
				expSW);
	}

	@JSFunction
	public void getChallenge(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_GET_CHALLENGE, p1, p2, data, le, expData, expSW);
	}

	public void terminalCapablity(String cla, String p1, String p2,
			String data, String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_TERMINAL_CAPABILITY, p1, p2, data, le, expData,
				expSW);
	}

	@JSFunction
	public void manageSecureChannel(String cla, String p1, String p2,
			String data, String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_MANAGE_SECURE_CHANNEL, p1, p2, data, le,
				expData, expSW);
	}

	@JSFunction
	public void transactData(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_TRANSACT_DATA, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void terminalProfile(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_TERMINAL_PROFILE, p1, p2, data, le, expData,
				expSW);
	}

	@JSFunction
	public void envelope(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_ENVELOPE, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void fetch(String cla, String p1, String p2, String data, String le,
			String expData, String expSW) {
		sendCommand(cla, JL.INS_FETCH, p1, p2, data, le, expData,
				expSW);
	}

	@JSFunction
	public void terminalResponse(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_TERMINAL_RESPONSE, p1, p2, data, le, expData,
				expSW);
	}

	@JSFunction
	public void retrieveData(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_RETRIEVE_DATA, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void setData(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_SET_DATA, p1, p2, data, le, expData, expSW);
	}

	@JSFunction
	public void getResponse(String cla, String p1, String p2, String data,
			String le, String expData, String expSW) {
		sendCommand(cla, JL.INS_GET_RESPONSE, p1, p2, data, le, expData, expSW);
	}
	
	@JSFunction
	public void sendCommand(String name, String cla, String ins, String p1,
			String p2, String data, String le, String expData, String expSW) {
		sendCommand(cla, HexUtil.parseHexToByte(ins), p1, p2, data, le,
				expData, expSW);
	}

	private void sendCommand(String cla, byte ins, String p1, String p2,
			String data, String le, String expData, String expSW) {
		
		if(data.equals("undefined")){
			data = "";
		}
		if(expSW.equals("undefined")){
			expSW = "";
		}
		if(expData.equals("undefined")){
			expData = "";
		}
		
		CommandAPDU cmd = makeCommandAPDU(cla, ins, p1, p2, data, le);
		ResponseAPDU res = card.sendCommandAPDU(cmd);
		String log = makeLog(cmd, expData, expSW);
		updateListenner(log);
		return;
		//checkSW(res.sw(), expSW);
		//checkData(res.data(), expData);
	}

	private void updateListenner(String log) {
		for(ICmdAndExpLog one : list){
			one.appendToLog(log);
		}
		
	}
	
	public void addListenner(ICmdAndExpLog log){
		this.list.add(log);
	}
	
	public void removeListenner(ICmdAndExpLog log){
		this.list.remove(log);
	}

	private CommandAPDU makeCommandAPDU(String cla, byte ins, String p1,
			String p2, String data, String le) {
		CommandAPDU cmd = new CommandAPDU(512);
		cmd.append(HexUtil.parseHexToByte(cla));
		cmd.append(ins);
		cmd.append(HexUtil.parseHexToByte(p1));
		cmd.append(HexUtil.parseHexToByte(p2));
		if(data!=null && data.trim().length() != 0){
			cmd.append((byte) 0x00);
			cmd.append(HexUtil.parseHexToByteArray(data));
			cmd.setByte(4, cmd.getLength() - 5);
		}
		if(le != null && le.trim().length()!=0){
			cmd.append(HexUtil.parseHexToByte(le));
		}
		return cmd;
	}

	private void checkData(byte[] data, String expData) throws UnexceptException {
		if (expData == null || expData.trim().equals("")) {
			return;
		}
		String strData = HexUtil.toHexString(data);
		if (!HexUtil.compare(expData, strData)) {
			throw new UnexceptException(expData, strData);
		}
	}

	private void checkSW(int sw, String expSW) throws UnexceptException {
		if (expSW == null || expSW.trim().equals("")) {
			return;
		}
		String strSW = HexUtil.toHex(sw, 2);
		if (!HexUtil.compare(expSW, strSW)) {
			throw new UnexceptException(expSW, strSW);
		}
	}
	
	private String makeLog(CommandAPDU cmd ,String expData,String expSW){
		StringBuilder sb = new StringBuilder();
		sb.append("CMD ").append(HexUtil.toHexString(cmd.getBytes()));
		if(!expData.trim().equals("")){
			sb.append("[").append(expData).append("]");
		}
		if(!expSW.trim().equals("") && !expSW.equals("undefined")){
			sb.append("(").append(expSW).append(")");
		}
		return sb.toString();
	}

	@Override
	public String getClassName() {
		return "CommandNoException";
	}
	
	public interface ICmdAndExpLog {
		
		void appendToLog(String log);

	}
}

class UnexceptException extends Exception {

	private static final long serialVersionUID = -7284859682047719885L;

	public UnexceptException(String exp, String real) {
		super("exp is :" + exp + ",real is :" + real);
	}
}
